#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/timer.h>
#include <linux/mutex.h>
#include <linux/fcntl.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/interrupt.h>

#define Timestamp_Driver_NUM          0
#define Timestamp_Driver_CNT			1		  	/* 设备号个数 */
#define Timestamp_Driver_NAME			"Timestamp_Driver"	/* 名字 */

typedef struct
{
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;		/* 类 		*/
	struct device *device;	/* 设备 	 */
	int major;				/* 主设备号	  */
	int minor;				/* 次设备号   */
    unsigned long long int Current_Offset;
    struct timer_list timer;
    struct mutex lock;
    struct fasync_struct *async_queue;
}Timestamp_Driver_dev;

Timestamp_Driver_dev Timestamp_Driver;

static void timer_callback(struct timer_list *arg)
{
    // printk("Timer Callback\n");
    kill_fasync(&Timestamp_Driver.async_queue, SIGIO, POLL_IN);
    mutex_unlock(&Timestamp_Driver.lock);
}

static int Init_Timestamp_Driver(unsigned char EnableNotDisable)
{
    if(EnableNotDisable)
    {
        mutex_init(&Timestamp_Driver.lock);
        mutex_lock(&Timestamp_Driver.lock);
        timer_setup(&Timestamp_Driver.timer, timer_callback, 0);
        mod_timer(&Timestamp_Driver.timer, jiffies + msecs_to_jiffies(10));
        printk("Driver Init\n");
    }
    else
    {
        mutex_unlock(&Timestamp_Driver.lock);
        del_timer_sync(&Timestamp_Driver.timer);
        printk("Driver UnInit\n");
    }

    return 0;
}

static loff_t Timestamp_Driver_llseek(struct file *filp, loff_t offt,  int cnt)
{
    ((Timestamp_Driver_dev*)(filp->private_data))->Current_Offset = offt;
	return 0;
}

static int Timestamp_Driver_open(struct inode *inode, struct file *filp)
{
    Timestamp_Driver.Current_Offset=0;
	filp->private_data = &Timestamp_Driver; /* 设置私有数据 */
    // printk("Driver Open\n");
	return 0;
}

static ssize_t Timestamp_Driver_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    return jiffies_to_msecs(jiffies);
}

static ssize_t Timestamp_Driver_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    return 0;
}

static long Timestamp_Driver_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    switch (cmd)
    {
        case 0x00:
        {
            while(arg--)
            {
                mdelay(1000);
            }
            break;
        }
        case 0x01:
        {
            mdelay(arg);
            break;
        }
        case 0x02:
        {
            udelay(arg);
            break;
        }
        case 0x03:
        {
            ndelay(arg);
            break;
        }
        case 0x10:
        {
            mutex_lock(&Timestamp_Driver.lock);
            mod_timer(&Timestamp_Driver.timer, jiffies + msecs_to_jiffies(arg*1000));
            break;
        }
        case 0x11:
        {
            mutex_lock(&Timestamp_Driver.lock);
            mod_timer(&Timestamp_Driver.timer, jiffies + msecs_to_jiffies(arg));
            break;
        }
         case 0x12:
        {
            mutex_lock(&Timestamp_Driver.lock);
            mod_timer(&Timestamp_Driver.timer, jiffies + usecs_to_jiffies(arg));
            break;
        }
         case 0x13:
        {
            mutex_lock(&Timestamp_Driver.lock);
            mod_timer(&Timestamp_Driver.timer, jiffies + nsecs_to_jiffies(arg));
            break;
        }
        default:
        {
            return 0;
        }
       
    }
    
    return cmd;
}

static int Timestamp_Driver_fasync(int fd, struct file *filp, int on)
{
    return fasync_helper(fd, filp, on, &((Timestamp_Driver_dev*)(filp->private_data))->async_queue);
}


/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int Timestamp_Driver_release(struct inode *inode, struct file *filp)
{
    // printk("Driver Close\n");
	return Timestamp_Driver_fasync(-1, filp, 0);;
}

/* 设备操作函数 */
static struct file_operations Timestamp_Driver_fops = {
	.owner = THIS_MODULE,
	.open = Timestamp_Driver_open,
	.read = Timestamp_Driver_read,
	.write = Timestamp_Driver_write,
	.release = 	Timestamp_Driver_release,
    .llseek = Timestamp_Driver_llseek,
    .unlocked_ioctl = Timestamp_Driver_unlocked_ioctl,
    .fasync = Timestamp_Driver_fasync,
};

static int __init Timestamp_Driver_init(void)
{
    int ret;    

    if (Timestamp_Driver_NUM) 
    {		/*  定义了设备号 */
        Timestamp_Driver.major = Timestamp_Driver_NUM;
		Timestamp_Driver.devid = MKDEV(Timestamp_Driver.major, 0);
		ret = register_chrdev_region(Timestamp_Driver.devid, Timestamp_Driver_CNT, Timestamp_Driver_NAME);
		if(ret < 0) {
			pr_err("cannot register %s char driver [ret=%d]\n",Timestamp_Driver_NAME, Timestamp_Driver_CNT);
			return -EIO;
		}
	} 
    else 
    {						/* 没有定义设备号 */
		ret = alloc_chrdev_region(&Timestamp_Driver.devid, 0, Timestamp_Driver_CNT, Timestamp_Driver_NAME);	/* 申请设备号 */
        if(ret < 0) {
            pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", Timestamp_Driver_NAME, ret);
            return -EIO;
        }
        Timestamp_Driver.major = MAJOR(Timestamp_Driver.devid);	/* 获取分配号的主设备号 */
        Timestamp_Driver.minor = MINOR(Timestamp_Driver.devid);	/* 获取分配号的次设备号 */
	}
    

    printk("Driver major=%d,minor=%d\r\n",Timestamp_Driver.major, Timestamp_Driver.minor);
    Timestamp_Driver.cdev.owner = THIS_MODULE;
    cdev_init(&Timestamp_Driver.cdev, &Timestamp_Driver_fops);

    ret = cdev_add(&Timestamp_Driver.cdev, Timestamp_Driver.devid, Timestamp_Driver_CNT);
	if(ret < 0)
    {
		unregister_chrdev_region(Timestamp_Driver.devid, Timestamp_Driver_CNT);
        return -EIO;
    }

    Timestamp_Driver.class = class_create(THIS_MODULE, Timestamp_Driver_NAME);
	if (IS_ERR(Timestamp_Driver.class)) 
    {
		cdev_del(&Timestamp_Driver.cdev);
        unregister_chrdev_region(Timestamp_Driver.devid, Timestamp_Driver_CNT);
        return -EIO;
	}

    Timestamp_Driver.device = device_create(Timestamp_Driver.class, NULL, Timestamp_Driver.devid, NULL, Timestamp_Driver_NAME);
	if (IS_ERR(Timestamp_Driver.device)) 
    {
		class_destroy(Timestamp_Driver.class);
        cdev_del(&Timestamp_Driver.cdev);
        unregister_chrdev_region(Timestamp_Driver.devid, Timestamp_Driver_CNT);
        return -EIO;
	}

    Init_Timestamp_Driver(1);
    return 0;
}

static void __exit Timestamp_Driver_exit(void)
{
    Init_Timestamp_Driver(0);
    cdev_del(&Timestamp_Driver.cdev);/*  删除cdev */
	unregister_chrdev_region(Timestamp_Driver.devid, Timestamp_Driver_CNT); /* 注销设备号 */
	device_destroy(Timestamp_Driver.class, Timestamp_Driver.devid);
	class_destroy(Timestamp_Driver.class);
}

module_init(Timestamp_Driver_init);
module_exit(Timestamp_Driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("MIKE_ZHOU");
MODULE_INFO(intree, "Y");
